<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><title>码行 - 与你同行</title><meta name="author" content="Carl Su"><meta name="copyright" content="Carl Su"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="ffffff"><meta name="description" content="纸上得来终觉浅，绝知此事要躬行。">
<meta property="og:type" content="website">
<meta property="og:title" content="码行">
<meta property="og:url" content="https://devsu.top/page/11/index.html">
<meta property="og:site_name" content="码行">
<meta property="og:description" content="纸上得来终觉浅，绝知此事要躬行。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://s2.loli.net/2023/03/09/QrI9mgJ4XlbEPzN.png">
<meta property="article:author" content="Carl Su">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://s2.loli.net/2023/03/09/QrI9mgJ4XlbEPzN.png"><link rel="shortcut icon" href="https://s2.loli.net/2023/03/09/whMHdYkrKoIBnSC.png"><link rel="canonical" href="https://devsu.top/page/11/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?6243425e2ecb037931274c4ca1db3132";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":true,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"简"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":300},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: {"limitCount":50,"languages":{"author":"作者: Carl Su","link":"链接: ","source":"来源: 码行","info":"著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"}},
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"bottom-left"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: true,
  islazyload: true,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: true,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '码行',
  isPost: false,
  isHome: true,
  isHighlightShrink: false,
  isToc: false,
  postUpdate: '2023-03-30 10:59:02'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', 'ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          const now = new Date()
          const hour = now.getHours()
          const isNight = hour <= 6 || hour >= 18
          if (t === undefined) isNight ? activateDarkMode() : activateLightMode()
          else if (t === 'light') activateLightMode()
          else activateDarkMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"></head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><script>const preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',()=> { preloader.endLoading() })

if (false) {
  document.addEventListener('pjax:send', () => { preloader.initLoading() })
  document.addEventListener('pjax:complete', () => { preloader.endLoading() })
}</script><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://s2.loli.net/2023/03/09/QrI9mgJ4XlbEPzN.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">169</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">37</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">38</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 清单</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="page" id="body-wrap"><header class="full_page fixed" id="page-header" style="background-image: url('https://s2.loli.net/2023/03/09/JQOG5BtENjoCvYl.jpg')"><nav id="nav"><span id="blog-info"><a href="/" title="码行"><img class="site-icon" src="https://s2.loli.net/2023/03/09/whMHdYkrKoIBnSC.png"/><span class="site-name">码行</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 清单</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="site-info"><h1 id="site-title">码行</h1><div id="site-subtitle"><span id="subtitle"></span></div><div id="site_social_icons"><a class="social-icon" href="https://github.com/winstone-su" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:winstone.su@gmail.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div id="scroll-down"><i class="fas fa-angle-down scroll-down-effects"></i></div></header><main class="layout" id="content-inner"><div class="recent-posts" id="recent-posts"><div class="recent-post-item"><div class="post_cover left"><a href="/posts/e184d855/" title="Kotlin编程第一课--(源码篇)28  launch的背后到底发生了什么？"><img class="post-bg" src="https://s2.loli.net/2023/03/14/AIcnqy15mGTQp8d.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(源码篇)28  launch的背后到底发生了什么？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/e184d855/" title="Kotlin编程第一课--(源码篇)28  launch的背后到底发生了什么？">Kotlin编程第一课--(源码篇)28  launch的背后到底发生了什么？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-24T08:56:36.000Z" title="发表于 2022-06-24 16:56:36">2022-06-24</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T08:00:57.086Z" title="更新于 2023-03-23 16:00:57">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">在前面的课程里，我们一直在研究如何使用 Kotlin 协程，比如，如何启动协程，如何使用挂起函数，如何使用 Channel、Flow 等 API。但到目前为止，我们只知道该怎么用协程，对它内部的运行机制并没有深究。
现在我们都知道，launch、async 可以创建、启动新的协程，但我们只能通过调试参数，通过 log 看到协程。比如我们可以回过头来看下第 13 讲当中的代码：
// 代码段1// 代码中一共启动了两个协程fun main() = runBlocking &#123;    println(Thread.currentThread().name)    launch &#123;        println(Thread.currentThread().name)        delay(100L)    &#125;    Thread.sleep(1000L)&#125;/*输出结果：main @coroutine#1main @coroutine#2*/

现在回过头来看，这段代码无疑是非常简单的，runBlocking{} 启动了第一个协程，launch{}  ...</div></div></div><div class="recent-post-item"><div class="post_cover right"><a href="/posts/875d6a31/" title="Kotlin编程第一课--(源码篇)27  图解挂起函数：原来你就是个状态机？"><img class="post-bg" src="https://imgs.aixifan.com/Fsuo2I-WK01Ki8DZTFF-XgHbv0Ea" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(源码篇)27  图解挂起函数：原来你就是个状态机？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/875d6a31/" title="Kotlin编程第一课--(源码篇)27  图解挂起函数：原来你就是个状态机？">Kotlin编程第一课--(源码篇)27  图解挂起函数：原来你就是个状态机？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-23T08:15:31.000Z" title="发表于 2022-06-23 16:15:31">2022-06-23</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T08:01:04.411Z" title="更新于 2023-03-23 16:01:04">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">今天我们来研究 Kotlin 挂起函数的实现原理。
挂起函数，是整个 Kotlin 协程的核心，它的重要性不言而喻。几乎所有协程里的知识点，都离不开挂起函数。而且也正是因为挂起函数的原因，我们才可以使用协程简化异步任务。
今天这节课，我会从这个 CPS 转换开始说起，带你进一步挖掘它背后的细节。在这个过程中，我们还会接触到 Kotlin 库当中的协程基础元素：Continuation、CoroutineContext 与挂起函数的底层联系。最后，我会带你灵活运用下这些知识点，以此进一步完善我们的 KtHttp，让它可以直接支持挂起函数。
好，接下来，我们就正式开始吧！
CPS 转换背后的细节在第 15 讲当中，我们已经初步介绍过挂起函数的用法了：挂起函数，只是比普通的函数多了 suspend 关键字。有了这个 suspend 关键字以后，Kotlin 编译器就会特殊对待这个函数，将其转换成一个带有 Callback 的函数，这里的 Callback 就是 Continuation 接口。
而这个过程，我们称之为 CPS 转换：


以上的 CPS 转换过程中，函数的类型发生了变化：su ...</div></div></div><div class="recent-post-item"><div class="post_cover left"><a href="/posts/80bc5780/" title="Kotlin编程第一课--(源码篇)26  协程源码的地图：如何读源码才不会迷失？"><img class="post-bg" src="https://gss0.baidu.com/9fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/4610b912c8fcc3ce195910729645d688d53f20d2.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(源码篇)26  协程源码的地图：如何读源码才不会迷失？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/80bc5780/" title="Kotlin编程第一课--(源码篇)26  协程源码的地图：如何读源码才不会迷失？">Kotlin编程第一课--(源码篇)26  协程源码的地图：如何读源码才不会迷失？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-22T08:10:19.000Z" title="发表于 2022-06-22 16:10:19">2022-06-22</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T08:01:09.582Z" title="更新于 2023-03-23 16:01:09">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">在前面学习协程的时候，我们说过协程是 Kotlin 里最重要、最难学的特性。之所以说协程重要，是因为它有千般万般的好：挂起函数、结构化并发、非阻塞、冷数据流，等等。不过协程也真的太抽象、太难学了。即使我们学完了前面的协程篇，知道了协程的用法，但也仍然远远不够，这种“知其然，不知其所以然”的感觉，总会让我们心里不踏实。
所以，我们必须搞懂 Kotlin 协程的源代码。
可问题是，协程的源码也非常复杂。如果你尝试研究过协程的源代码，那你对此一定深有体会。在 Kotlin 协程 1.6.0 版本中，仅仅是协程跟 JVM 相关的源代码，就有 27789 行。如果算上 JavaScript 平台、Native 平台，以及单元测试相关的代码，Kotlin 协程库当中的源代码有接近 10 万行。面对这么多的源代码，我们根本不可能一行一行去分析。
因此，我们在研究 Kotlin 协程的源代码的时候，要有一定的技巧。这里给你分享我的两个小技巧：

理解 Kotlin 协程的源码结构。Kotlin 协程的源代码分布在多个模块之中，每个模块都会包含特定的协程概念。相应的，它的各个概念也有特定的层级结构，只有 ...</div></div></div><div class="recent-post-item"><div class="post_cover right"><a href="/posts/e47f1d17/" title="Kotlin编程第一课--(源码篇)25  集合操作符：你也会“看完就忘”吗？"><img class="post-bg" src="https://s2.loli.net/2023/03/14/lYqWnptIy2VEKAv.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(源码篇)25  集合操作符：你也会“看完就忘”吗？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/e47f1d17/" title="Kotlin编程第一课--(源码篇)25  集合操作符：你也会“看完就忘”吗？">Kotlin编程第一课--(源码篇)25  集合操作符：你也会“看完就忘”吗？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-21T07:38:39.000Z" title="发表于 2022-06-21 15:38:39">2022-06-21</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T08:01:16.555Z" title="更新于 2023-03-23 16:01:16">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">从这节课开始，我们就正式进入源码篇的学习了。当我们学习一门知识的时候，总
是离不开 What、Why 和 How。在前面的基础篇、协程篇当中，我们已经弄清楚了 Kotlin 是什么，以及为什么要用 Kotlin。那么在这个模块里，我们主要是来解决 How 的问题，以此从根源上搞清楚 Kotlin 的底层实现原理。今天这节课，我们先来搞定集合操作符的用法与原理。
对于大部分 Java、C 开发者来说，可能都会对 Kotlin 的集合操作符感到头疼，因为它们实在太多、太乱了。即使通过 Kotlin 官方文档把那些操作符一个个过了一遍，但过一段时间在代码中遇到它们，又会觉得陌生。一看就会，看完就忘！
其实，Kotlin 的集合 API，本质上是一种数据处理的模式。
什么是数据处理模式？可以想象一下：对于 110 的数字来说，我们找出其中的偶数，那么这就是一种过滤的行为。我们计算出 110 的总和，那么这就是一种求和的行为。所以从数据操作的角度来看，Kotlin 的操作符就可以分为几个大类：过滤、转换、分组、分割、求和。
那么接下来，我会根据一个统计学生成绩的案例，来带你分析 Kotlin 的 ...</div></div></div><div class="recent-post-item"><div class="post_cover left"><a href="/posts/85d82fa8/" title="Kotlin编程第一课--(答疑篇)答疑（一）| Java和Kotlin到底谁好谁坏？"><img class="post-bg" src="https://img0.baidu.com/it/u=1043079064,1717028606&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=800&amp;h=500" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(答疑篇)答疑（一）| Java和Kotlin到底谁好谁坏？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/85d82fa8/" title="Kotlin编程第一课--(答疑篇)答疑（一）| Java和Kotlin到底谁好谁坏？">Kotlin编程第一课--(答疑篇)答疑（一）| Java和Kotlin到底谁好谁坏？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-10T08:30:08.000Z" title="发表于 2022-06-10 16:30:08">2022-06-10</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T07:59:17.545Z" title="更新于 2023-03-23 15:59:17">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">由于咱们课程的设计理念是简单易懂、贴近实际工作，所以我在课程内容的讲述上也会有一些侧重点，进而也会忽略一些细枝末节的知识点。不过，我看到很多同学都在留言区分享了自己的见解，算是对课程内容进行了很好的补充，这里给同学们点个赞，感谢你的仔细思考和认真学习。
另外，我看到不少同学提出的很多问题也都非常有价值，有些问题非常有深度，有些问题非常有实用性，有些问题则非常有代表性，这些问题也值得我们再一起探讨下。因此，这一次，我们来一次集中答疑。
Java 和 Kotlin 到底谁好谁坏？

很多同学看完开篇词以后，可能会留下一种印象，就是貌似 Java 就是坏的，Kotlin 就是好的。但其实在我看来，语言之间是不存在明确的优劣之分的。“XX 是世界上最好的编程语言”这种说法，也是没有任何意义的。
不过，虽然语言之间没有优劣之分，但在特定场景下，还是会有更优选择的。比如说，站在 Android 开发的角度上看，Kotlin 就的确要比 Java 强很多；但如果换一个角度，服务端开发，Kotlin 的优势则并不明显，因为 Spring Boot 之类的框架对 Java 的支持已经足够好了；甚至，如果 ...</div></div></div><div class="recent-post-item"><div class="post_cover right"><a href="/posts/6532289/" title="Kotlin编程第一课--(协程篇)24  实战：让KtHttp支持Flow"><img class="post-bg" src="http://t15.baidu.com/it/u=1016075162,2061563941&amp;fm=224&amp;app=112&amp;f=JPEG?w=354&amp;h=500" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(协程篇)24  实战：让KtHttp支持Flow"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/6532289/" title="Kotlin编程第一课--(协程篇)24  实战：让KtHttp支持Flow">Kotlin编程第一课--(协程篇)24  实战：让KtHttp支持Flow</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-04T07:04:50.000Z" title="发表于 2022-06-04 15:04:50">2022-06-04</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T07:58:56.021Z" title="更新于 2023-03-23 15:58:56">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">Kotlin编程第一课–(协程篇)24 | 实战：让KtHttp支持Flow又到了熟悉的实战环节，这一次我们接着来改造 KtHttp，让它能够支持协程的 Flow API。
有了前面两次实战的基础，这次我们应该就轻车熟路了。在之前的4.0 版本中，为了让 KtHttp 支持挂起函数，我们有两种思路，一种是改造内部，另一种是扩展外部。同理，为了让 KtHttp 支持 Flow，这次的实战也是这两种思路。
因此，这节课我们仍然会分为两个版本。

5.0 版本，基于 4.0 版本的代码，从 KtHttp 的外部扩展出 Flow 的能力。

6.0 版本，修改 KtHttp 内部，让它支持 Flow API。


其实在实际的工作中，我们往往没有权限修改第三方提供的 SDK，那么这时候，如果想要让 SDK 获得 Flow 的能力，我们就只能借助 Kotlin 的扩展函数，为它扩展出 Flow 的能力。而对于工程内部的代码，我们希望某个功能模块获得 Flow 的能力，就可以直接修改它的源代码，让它直接支持 Flow。
那么在这节课里，我会同时用这两种手段来扩展并改造 KtHttp，为你演示其中的 ...</div></div></div><div class="recent-post-item"><div class="post_cover left"><a href="/posts/a3460e51/" title="Kotlin编程第一课--(协程篇)23  异常：try-catch 坑！"><img class="post-bg" src="http://t15.baidu.com/it/u=2335792902,1551903918&amp;fm=224&amp;app=112&amp;f=JPEG?w=500&amp;h=445" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(协程篇)23  异常：try-catch 坑！"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/a3460e51/" title="Kotlin编程第一课--(协程篇)23  异常：try-catch 坑！">Kotlin编程第一课--(协程篇)23  异常：try-catch 坑！</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-03T02:40:44.000Z" title="发表于 2022-06-03 10:40:44">2022-06-03</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T07:58:46.668Z" title="更新于 2023-03-23 15:58:46">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">这节课我们来学习 Kotlin 协程的异常处理。
其实到这里，我们就已经学完所有 Kotlin 协程的语法知识了。但在真正把 Kotlin 协程应用到生产环境之前，我们还需要掌握一个重要知识点，那就是异常处理。
比起 Kotlin 协程的语法知识点，协程的异常处理，其实更难掌握。在前面的课程中，我们已经了解到：协程就是互相协作的程序，协程是结构化的。正因为 Kotlin 协程有这两个特点，这就导致它的异常处理机制与我们普通的程序完全不一样。
换句话说：如果把 Java 里的那一套异常处理机制，照搬到 Kotlin 协程里来，你一定会四处碰壁。因为在普通的程序当中，你使用 try-catch 就能解决大部分的异常处理问题，但是在协程当中，根据不同的协程特性，它的异常处理策略是随之变化的。
我自己在工作中就踩过很多这方面的坑，遇到过各种匪夷所思的问题：协程无法取消、try-catch 不起作用导致线上崩溃率突然大增、软件功能错乱却追踪不到任何异常信息，等等。说实话，Kotlin 协程的普及率之所以不高，很大一部分原因也是因为它的异常处理机制太复杂了，稍有不慎就可能会掉坑里去。
那么今天这节 ...</div></div></div><div class="recent-post-item"><div class="post_cover right"><a href="/posts/778b5847/" title="Kotlin编程第一课--(协程篇)22  并发：协程不需要处理同步吗？"><img class="post-bg" src="https://img.tt98.com/d/file/pic/2018060523545938/5b15f4a3092f0.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(协程篇)22  并发：协程不需要处理同步吗？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/778b5847/" title="Kotlin编程第一课--(协程篇)22  并发：协程不需要处理同步吗？">Kotlin编程第一课--(协程篇)22  并发：协程不需要处理同步吗？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-02T02:38:44.000Z" title="发表于 2022-06-02 10:38:44">2022-06-02</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T07:58:34.934Z" title="更新于 2023-03-23 15:58:34">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">
今天我们来讲讲协程的并发。
在大型软件的架构当中，并发也是一个不可避免的问题。然而，在传统的 Java 编程当中，并发却是个令人生畏的话题。因为 Java 的线程模型、内存模型、同步机制太复杂了，而当复杂的业务逻辑与复杂的并发模型混合在一起的时候，情况就更糟糕了！如果你用 Java 做过中大型软件，对此一定会深有体会。
我们都知道，Kotlin 的协程仍然是基于线程运行的。但是，经过一层封装以后，Kotlin 协程面对并发问题的时候，它的处理手段其实跟 Java 就大不一样。所以这节课，我们就来看看协程在并发问题上的处理，一起来探究下 Kotlin 协程的并发思路，从而真正解决并发的难题。
协程与并发在 Java 世界里，并发往往需要多个线程一起工作，而多线程往往就会有共享的状态，这时候程序就要处理同步问题了。很多初学者在这一步，都会把协程与线程的概念混淆在一起。比如你可以来看看下面这段代码，你觉得有多线程同步的问题吗？
// 代码段1fun main() = runBlocking &#123;    var i = 0    // Default 线程池    launch(Di ...</div></div></div><div class="recent-post-item"><div class="post_cover left"><a href="/posts/4b3d9f4d/" title="Kotlin编程第一课--(协程篇)21  select：到底是在选择什么？"><img class="post-bg" src="https://pic.rmb.bdstatic.com/66c29f8df73f201a1b3f3f1661ccf957.jpeg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(协程篇)21  select：到底是在选择什么？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/4b3d9f4d/" title="Kotlin编程第一课--(协程篇)21  select：到底是在选择什么？">Kotlin编程第一课--(协程篇)21  select：到底是在选择什么？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-01T02:13:38.000Z" title="发表于 2022-06-01 10:13:38">2022-06-01</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-23T07:58:25.136Z" title="更新于 2023-03-23 15:58:25">2023-03-23</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">
今天我们来学习 Kotlin 协程的 select。
select，在目前的 Kotlin 1.6 当中，仍然是一个实验性的特性（Experimental）。但是，考虑到 select 具有较强的实用性，我决定还是来给你介绍一下它。
select 可以说是软件架构当中非常重要的一个组件，在很多业务场景下，select 与 Deferred、Channel 结合以后，在大大提升程序的响应速度的同时，还可以提高程序的灵活性、扩展性。
今天这节课，我会从 select 的使用角度着手，带你理解 select 的核心使用场景，之后也会通过源码帮你进一步分析 select API 的底层规律。学完这节课以后，你完全可以将 select 应用到自己的工作当中去。
好，接下来，我们就一起来学习 select 吧！
select 就是选择“更快的结果”

由于 select 的工作机制比较抽象，我们先来假设一个场景，看看 select 适用于什么样的场景。
客户端，想要查询一个商品的详情。目前有两个服务：缓存服务，速度快但信息可能是旧的；网络服务，速度慢但信息一定是最新的。

对于这个场景，如果让我 ...</div></div></div><div class="recent-post-item"><div class="post_cover right"><a href="/posts/e7c23ba1/" title="Kotlin编程第一课--(协程篇)20  Flow：为什么说Flow是“冷”的？"><img class="post-bg" src="https://img1.baidu.com/it/u=4050062314,1116360005&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=889&amp;h=500" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Kotlin编程第一课--(协程篇)20  Flow：为什么说Flow是“冷”的？"></a></div><div class="recent-post-info"><a class="article-title" href="/posts/e7c23ba1/" title="Kotlin编程第一课--(协程篇)20  Flow：为什么说Flow是“冷”的？">Kotlin编程第一课--(协程篇)20  Flow：为什么说Flow是“冷”的？</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-05-30T01:51:38.000Z" title="发表于 2022-05-30 09:51:38">2022-05-30</time><span class="article-meta-separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-03-27T02:56:47.917Z" title="更新于 2023-03-27 10:56:47">2023-03-27</time></span><span class="article-meta"><span class="article-meta-separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/Android/">Android</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/">Kotlin</a><i class="fas fa-angle-right article-meta-link"></i><a class="article-meta__categories" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/">Kotlin编程第一课</a></span><span class="article-meta tags"><span class="article-meta-separator">|</span><i class="fas fa-tag"></i><a class="article-meta__tags" href="/tags/Kotlin/">Kotlin</a></span></div><div class="content">今天我们来学习 Kotlin 协程 Flow 的基础知识。
Flow，可以说是在 Kotlin 协程当中自成体系的知识点。Flow 极其强大、极其灵活，在它出现之前，业界还有很多质疑 Kotlin 协程的声音，认为 Kotlin 的挂起函数、结构化并发，并不足以形成核心竞争力，在异步、并发任务的领域，RxJava 可以做得更好。
但是，随着 2019 年 Kotlin 推出 Flow 以后，这样的质疑声就渐渐没有了。有了 Flow 以后，Kotlin 的协程已经没有明显的短板了。简单的异步场景，我们可以直接使用挂起函数、launch、async；至于复杂的异步场景，我们就可以使用 Flow。
实际上，在很多技术领域，Flow 已经开始占领 RxJava 原本的领地，在 Android 领域，Flow 甚至还要取代原本 LiveData 的地位。因为，Flow 是真的香啊！
接下来，我们就一起来学习 Flow。
Flow 就是“数据流”Flow 这个单词有“流”的意思，比如 Cash Flow 代表了“现金流”；Traffic Flow 代表了“车流”；Flow 在 Kotlin 协程当 ...</div></div></div><nav id="pagination"><div class="pagination"><a class="extend prev" rel="prev" href="/page/10/#content-inner"><i class="fas fa-chevron-left fa-fw"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/10/#content-inner">10</a><span class="page-number current">11</span><a class="page-number" href="/page/12/#content-inner">12</a><span class="space">&hellip;</span><a class="page-number" href="/page/17/#content-inner">17</a><a class="extend next" rel="next" href="/page/12/#content-inner"><i class="fas fa-chevron-right fa-fw"></i></a></div></nav></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://s2.loli.net/2023/03/09/QrI9mgJ4XlbEPzN.png" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">Carl Su</div><div class="author-info__description">纸上得来终觉浅，绝知此事要躬行。</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">169</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">37</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">38</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/winstone-su" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:winstone.su@gmail.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">学习如逆水行舟，不进则退。</div></div><div class="sticky_layout"><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/posts/7098ebd6/" title="2023-03-30-Windows下PowerShell自定义函数"><img src="https://s2.loli.net/2023/03/30/6ilJQwW3T2e89xy.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="2023-03-30-Windows下PowerShell自定义函数"/></a><div class="content"><a class="title" href="/posts/7098ebd6/" title="2023-03-30-Windows下PowerShell自定义函数">2023-03-30-Windows下PowerShell自定义函数</a><time datetime="2023-03-30T01:57:31.000Z" title="发表于 2023-03-30 09:57:31">2023-03-30</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/e6af74ea/" title="2023-03-30-Funny Github">2023-03-30-Funny Github</a><time datetime="2023-03-30T01:12:25.000Z" title="发表于 2023-03-30 09:12:25">2023-03-30</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/posts/7028d55e/" title="2023-03-29-CE修改器加速百度网盘下载"><img src="https://img1.mydrivers.com/img/20210916/ecbb517f-6cd5-438f-9efc-98e9973eece3.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="2023-03-29-CE修改器加速百度网盘下载"/></a><div class="content"><a class="title" href="/posts/7028d55e/" title="2023-03-29-CE修改器加速百度网盘下载">2023-03-29-CE修改器加速百度网盘下载</a><time datetime="2023-03-29T01:48:38.000Z" title="发表于 2023-03-29 09:48:38">2023-03-29</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/posts/bf1f0a14/" title="2023-03-27-Hilt基本用法">2023-03-27-Hilt基本用法</a><time datetime="2023-03-27T09:14:16.000Z" title="发表于 2023-03-27 17:14:16">2023-03-27</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/posts/6f434e78/" title="2023-03-23-CentOS新建定时任务"><img src="https://s2.loli.net/2023/03/23/KLmkEbpJArO4z1H.webp" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="2023-03-23-CentOS新建定时任务"/></a><div class="content"><a class="title" href="/posts/6f434e78/" title="2023-03-23-CentOS新建定时任务">2023-03-23-CentOS新建定时任务</a><time datetime="2023-03-23T03:08:39.000Z" title="发表于 2023-03-23 11:08:39">2023-03-23</time></div></div></div></div><div class="card-widget card-categories"><div class="item-headline">
            <i class="fas fa-folder-open"></i>
            <span>分类</span>
            <a class="card-more-btn" href="/categories/" title="查看更多">
    <i class="fas fa-angle-right"></i></a>
            </div>
            <ul class="card-category-list" id="aside-cat-list">
            <li class="card-category-list-item "><a class="card-category-list-link" href="/categories/AI/"><span class="card-category-list-name">AI</span><span class="card-category-list-count">1</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/"><span class="card-category-list-name">Android</span><span class="card-category-list-count">100</span></a><ul class="card-category-list child"><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/Jetpack/"><span class="card-category-list-name">Jetpack</span><span class="card-category-list-count">13</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/Kotlin/"><span class="card-category-list-name">Kotlin</span><span class="card-category-list-count">67</span></a><ul class="card-category-list child"><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/Kotlin/Coroutine/"><span class="card-category-list-name">Coroutine</span><span class="card-category-list-count">2</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/Kotlin/Foundation/"><span class="card-category-list-name">Foundation</span><span class="card-category-list-count">16</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/Kotlin/Kotlin%E7%BC%96%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE/"><span class="card-category-list-name">Kotlin编程第一课</span><span class="card-category-list-count">48</span></a></li></ul></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Android/%E8%87%AA%E5%AE%9A%E4%B9%89View/"><span class="card-category-list-name">自定义View</span><span class="card-category-list-count">4</span></a></li></ul></li>
            </ul></div><div class="card-widget card-tags"><div class="item-headline"><i class="fas fa-tags"></i><span>标签</span></div><div class="card-tag-cloud"><a href="/tags/Kotlin/" style="font-size: 1.45em; color: rgb(195, 93, 54)">Kotlin</a><a href="/tags/git/" style="font-size: 1.18em; color: rgb(135, 190, 174)">git</a><a href="/tags/Android/" style="font-size: 1.42em; color: rgb(101, 90, 154)">Android</a><a href="/tags/JitPack/" style="font-size: 1.15em; color: rgb(143, 142, 114)">JitPack</a><a href="/tags/Retrofit/" style="font-size: 1.15em; color: rgb(90, 150, 44)">Retrofit</a><a href="/tags/Hexo/" style="font-size: 1.32em; color: rgb(51, 109, 10)">Hexo</a><a href="/tags/Markdown/" style="font-size: 1.18em; color: rgb(133, 57, 59)">Markdown</a><a href="/tags/iOS/" style="font-size: 1.35em; color: rgb(121, 123, 114)">iOS</a><a href="/tags/MacOS/" style="font-size: 1.18em; color: rgb(10, 35, 122)">MacOS</a><a href="/tags/Http/" style="font-size: 1.15em; color: rgb(94, 39, 115)">Http</a><a href="/tags/Python/" style="font-size: 1.25em; color: rgb(45, 148, 172)">Python</a><a href="/tags/jekyll/" style="font-size: 1.15em; color: rgb(85, 108, 165)">jekyll</a><a href="/tags/Jekyll/" style="font-size: 1.15em; color: rgb(170, 25, 64)">Jekyll</a><a href="/tags/postman/" style="font-size: 1.15em; color: rgb(61, 161, 159)">postman</a><a href="/tags/AI/" style="font-size: 1.15em; color: rgb(7, 49, 2)">AI</a><a href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" style="font-size: 1.22em; color: rgb(135, 140, 105)">设计模式</a><a href="/tags/jenkins/" style="font-size: 1.15em; color: rgb(194, 115, 151)">jenkins</a><a href="/tags/linux/" style="font-size: 1.15em; color: rgb(138, 124, 34)">linux</a><a href="/tags/Charles/" style="font-size: 1.15em; color: rgb(128, 13, 38)">Charles</a><a href="/tags/harmmony/" style="font-size: 1.15em; color: rgb(171, 37, 138)">harmmony</a><a href="/tags/google-services/" style="font-size: 1.15em; color: rgb(28, 164, 123)">google services</a><a href="/tags/Selenium/" style="font-size: 1.15em; color: rgb(134, 3, 111)">Selenium</a><a href="/tags/Jetpack/" style="font-size: 1.38em; color: rgb(16, 30, 154)">Jetpack</a><a href="/tags/kotlin/" style="font-size: 1.28em; color: rgb(172, 46, 140)">kotlin</a><a href="/tags/gradle/" style="font-size: 1.15em; color: rgb(21, 115, 7)">gradle</a><a href="/tags/Android-Studio/" style="font-size: 1.15em; color: rgb(12, 18, 68)">Android Studio</a><a href="/tags/cesium/" style="font-size: 1.18em; color: rgb(26, 81, 37)">cesium</a><a href="/tags/arcgis-for-iOS/" style="font-size: 1.15em; color: rgb(12, 200, 16)">arcgis for iOS</a><a href="/tags/arcgis/" style="font-size: 1.28em; color: rgb(81, 159, 27)">arcgis</a><a href="/tags/Arcgis-for-Android/" style="font-size: 1.15em; color: rgb(59, 79, 47)">Arcgis for Android</a><a href="/tags/Swift/" style="font-size: 1.22em; color: rgb(42, 181, 64)">Swift</a><a href="/tags/Coroutine/" style="font-size: 1.15em; color: rgb(198, 124, 11)">Coroutine</a><a href="/tags/SwiftUI/" style="font-size: 1.22em; color: rgb(1, 175, 42)">SwiftUI</a><a href="/tags/ViewModel/" style="font-size: 1.15em; color: rgb(183, 106, 135)">ViewModel</a><a href="/tags/tools/" style="font-size: 1.15em; color: rgb(31, 78, 33)">tools</a><a href="/tags/github/" style="font-size: 1.15em; color: rgb(121, 106, 125)">github</a><a href="/tags/powershell/" style="font-size: 1.15em; color: rgb(14, 0, 155)">powershell</a></div></div><div class="card-widget card-archives"><div class="item-headline"><i class="fas fa-archive"></i><span>归档</span><a class="card-more-btn" href="/archives/" title="查看更多">
    <i class="fas fa-angle-right"></i></a></div><ul class="card-archive-list"><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2023/03/"><span class="card-archive-list-date">三月 2023</span><span class="card-archive-list-count">22</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2023/02/"><span class="card-archive-list-date">二月 2023</span><span class="card-archive-list-count">10</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2023/01/"><span class="card-archive-list-date">一月 2023</span><span class="card-archive-list-count">11</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2022/12/"><span class="card-archive-list-date">十二月 2022</span><span class="card-archive-list-count">2</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2022/11/"><span class="card-archive-list-date">十一月 2022</span><span class="card-archive-list-count">2</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2022/10/"><span class="card-archive-list-date">十月 2022</span><span class="card-archive-list-count">3</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2022/09/"><span class="card-archive-list-date">九月 2022</span><span class="card-archive-list-count">3</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2022/08/"><span class="card-archive-list-date">八月 2022</span><span class="card-archive-list-count">37</span></a></li></ul></div><div class="card-widget card-webinfo"><div class="item-headline"><i class="fas fa-chart-line"></i><span>网站资讯</span></div><div class="webinfo"><div class="webinfo-item"><div class="item-name">文章数目 :</div><div class="item-count">169</div></div><div class="webinfo-item"><div class="item-name">已运行时间 :</div><div class="item-count" id="runtimeshow" data-publishDate="2015-07-07T16:00:00.000Z"><i class="fa-solid fa-spinner fa-spin"></i></div></div><div class="webinfo-item"><div class="item-name">本站总字数 :</div><div class="item-count">289.9k</div></div><div class="webinfo-item"><div class="item-name">本站访客数 :</div><div class="item-count" id="busuanzi_value_site_uv"><i class="fa-solid fa-spinner fa-spin"></i></div></div><div class="webinfo-item"><div class="item-name">本站总访问量 :</div><div class="item-count" id="busuanzi_value_site_pv"><i class="fa-solid fa-spinner fa-spin"></i></div></div><div class="webinfo-item"><div class="item-name">最后更新时间 :</div><div class="item-count" id="last-push-date" data-lastPushDate="2023-03-30T02:59:01.282Z"><i class="fa-solid fa-spinner fa-spin"></i></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2018 - 2023 By Carl Su</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">学无止境</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button id="chat_btn" type="button" title="聊天"><i class="fas fa-sms"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><div class="js-pjax"><script>window.typedJSFn = {
  init: (str) => {
    window.typed = new Typed('#subtitle', Object.assign({
      strings: str,
      startDelay: 300,
      typeSpeed: 150,
      loop: true,
      backSpeed: 50,
    }, null))
  },
  run: (subtitleType) => {
    if (true) {
      if (typeof Typed === 'function') {
        subtitleType()
      } else {
        getScript('https://cdn.jsdelivr.net/npm/typed.js/lib/typed.min.js').then(subtitleType)
      }
    } else {
      subtitleType()
    }
  }
}
</script><script>function subtitleType () {
  if (true) {
    typedJSFn.init(["Better a witty fool than a foolish wit. -- Shakespeare"])
  } else {
    document.getElementById("subtitle").innerHTML = 'Better a witty fool than a foolish wit. -- Shakespeare'
  }
}
typedJSFn.run(subtitleType)</script></div><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-fluttering-ribbon.min.js"></script><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/activate-power-mode.min.js"></script><script>POWERMODE.colorful = true;
POWERMODE.shake = false;
POWERMODE.mobile = false;
document.body.addEventListener('input', POWERMODE);
</script><script id="click-heart" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/click-heart.min.js" async="async" mobile="false"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div><script src="/js/search/local-search.js"></script></div></body></html>